﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace MessageServer.channels
{
    /**
     * The listener to state changes in the eventtracker for
     * an operation that is running on the eventtracker
     * */
    public interface EventStateListener
    {
        //implement the functionality that has to be called when a state changes
        void statechanged(EventTracker state);
    }

    /**
     * The tracker object for the event state.
     * */
    public interface EventTracker
    {
        bool succeeded { get; set; }        //event succeeded
        bool done { get; set; }             //event is done
        bool cancelled { get; set; }        //event is cancelled
        Exception failure { get; set; }     //event failed
        HandlerState result { get; set; } //the result of the last handler run

        void addListener(EventStateListener listener);  //add a listener
        void removeListener(EventStateListener listener);   //remove the listener
        bool waittofinish(int millis);                      //wait to finish.
    }

    /**
     * The actual event interface. All events should implement this 
     * interface.
     * */
    public interface ChannelEvent
    {
        Channel channel { get; }        //channel for this event
        EventTracker tracker { get; }   //the tracker for this event
    }

    /**
     * Implement this in case it is a message event. This contains the message
     * to be passed across.
     * */
    public interface MessageEvent : ChannelEvent
    {
        Object getMessage();    //the message of this event.
        void transformMessage(Object msg);    //transform the message as this.
    }

    /**
     * A default implementation of the event tracker. This will notify all the
     * registered listeners when the eventtracker says that it is done.
     * */
    public class DefaultEventTracker : EventTracker
    {
        bool _succeeded;                //flag to indicate the event succeeded
        bool _done;                     //flag to indicate that the event is done
        bool _cancelled;                //flag to indicate that the event is cancelled
        Exception _exception;           //flag to indicate that the event had an exception
        ChannelEvent _event;            //I am tracking this channel event
        HandlerState _lastresult;      //latest handler result

        object _waitobject = new object();
        HashSet<EventStateListener> _listeners = new HashSet<EventStateListener>(); //listeners registered

        //helper function to reset this eventtracker
        private void reset()
        {
            _succeeded = false;
            _done = false;
            _cancelled = false;
            _exception = null;
            _listeners.Clear();
            _lastresult = HandlerState.gonext;
        }

        //helper function to notify all listeners if any present
        private void notifydone()
        {
            lock (_waitobject)
            {
                Monitor.PulseAll(_waitobject); //notify anyone waiting on this.
            }

            foreach (EventStateListener listener in _listeners)
            {
                listener.statechanged(this); //do we want to thread this, maybe.
            }
        }

        /**
         * Create a tracker for the evt passed.
         * */
        public DefaultEventTracker(ChannelEvent evt)
        {
            _event = evt;
            reset();
        }

        //set up succeeded/cancelled/failure then call done to notify waiting parties.
        public bool succeeded { get { return _succeeded; } set { _succeeded = value; } }
        public bool done { get { return _done; } set { _done = value; notifydone(); } }
        public bool cancelled { get { return _cancelled;} set { _cancelled = value;} }
        public Exception failure { get { return _exception;} set { _exception = value;} }
        public HandlerState result { get { return _lastresult; } set { _lastresult = value; } } //the result of the last handler run

        public void addListener(EventStateListener listener)
        {
            _listeners.Add(listener);
        }

        public void removeListener(EventStateListener listener)
        {
            _listeners.Remove(listener);
        }

        public bool waittofinish(int millis)
        {
            bool ret = false;
            lock (_waitobject)
            {
                ret = Monitor.Wait(_waitobject, millis);
            }

            return ret;
        }

    }

    /**
     * An Event implementation to indicate state of the channel. The states in
     * which the channel can be present are OPEN, CLOSED, BOUND, CONNECTED, RECEIVED
     * SENT.
     * */
    public class ChannelStateEvent : ChannelEvent
    {
        //enum to indicate states
        public enum ChannelState { OPEN, CLOSED, BOUND, CONNECTED, RECEIVED, SENT }

        Channel _channel;           //channel for this event
        EventTracker _tracker;      //tracker for this event
        ChannelState _state;        //state of the channel
        Object _value;              //value indicating based on the state

        //construct stateevent for the given channel and state
        public ChannelStateEvent(Channel channel, ChannelState state)
        {
            _channel = channel;
            _tracker = ChannelUtils.getEventTracker(this);
            _state = state;
        }

        //construct stateevent for the given channel and state and value
        public ChannelStateEvent(Channel channel, ChannelState state, Object value)
        {
            _channel = channel;
            _tracker = ChannelUtils.getEventTracker(this);
            _state = state;
            _value = value;
        }

        //attributes to access various values.
        public EventTracker tracker { get { return _tracker; } }
        public Channel channel { get { return _channel; } }
        public ChannelState state { get { return _state; } }
        public Object value { get { return _value; } }
    }

    //Downstream state event
    public class DownstreamStateEvent : ChannelStateEvent
    {
        public DownstreamStateEvent(Channel channel, ChannelState state, Object value)
            : base(channel, state, value)
        {
        }
    }

    public class DefaultMessageEvent : MessageEvent
    {
        Channel _channel;           //Channel in which the message was generated
        EventTracker _tracker;      //tracker to track this message
        Object _message;            //message on this event

        //construct for the given channel and msg
        public DefaultMessageEvent(Channel channel, Object msg)
        {
            _channel = channel;
            _message = msg;
            _tracker = ChannelUtils.getEventTracker(this);
        }

        //return the message
        public Object getMessage()
        {
            return _message;
        }

        public void transformMessage(Object obj)
        {
            _message = obj; //allows for the message to modified as it flow thru the pipeline
        }

        //return the channel and tracker for this event
        public Channel channel { get { return _channel; } }
        public EventTracker tracker { get { return _tracker; } }
    }

    //upstream Message event. used for data received on the channel
    public class UpstreamMessageEvent : DefaultMessageEvent
    {
        //construct for the given channel and msg
        public UpstreamMessageEvent(Channel channel, Object msg) : base(channel, msg)
        {
        }
    }

    //downstreammsg events. Used for writing into the channel
    public class DownStreamMsgEvent : DefaultMessageEvent
    {
        ChannelEvent _sourceEvent;
        Object _inReplyTo;

        //construct for the given channel and msg
        public DownStreamMsgEvent(ChannelEvent src, Object srcobj, Object msg) : base(src.channel, msg)
        {
            _inReplyTo = srcobj;
            _sourceEvent = src;
        }

        public Object inReplyTo { get { return _inReplyTo; } }
        public ChannelEvent sourceEvent { get { return _sourceEvent; } }
    }

    /**
     * Event that will be fired when there is an exception in any code execution path
     * in the channel.
     * */
    public class ExceptionEvent : ChannelEvent
    {
        Channel _channel;               //channel that was being processed when exception occurred
        EventTracker _tracker;          //The event tracker for this event
        Exception _exception;           //The exception that was raised and caught
        ChannelEvent _exceptEvent;      //The channel event that was being processed when exception occurred

        //The evt can be null, in which case there was no event the normal flow caused a problem
        public ExceptionEvent(Channel channel, Exception e, ChannelEvent evt)
        {
            _channel = channel;
            _exception = e;
            _exceptEvent = evt;
            _tracker = ChannelUtils.getEventTracker(this);
        }

        //accessor functions to the various event parameters
        public Channel channel { get { return _channel; } }
        public EventTracker tracker { get { return _tracker; } }
        public Exception exception { get { return _exception; } }
        public ChannelEvent exceptionevent { get { return _exceptEvent; } }
    }
}
